#pragma once

#include<functional>
#include<iostream>

using namespace std;

namespace xyx
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		~auto_ptr()
		{
			delete _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		auto_ptr(const auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}
	private:
		T* _ptr;
	};

	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

		~unique_ptr()
		{
			delete _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		unique_ptr(const auto_ptr<T>& ap) = delete;
		unique_ptr<T>& operator=(const auto_ptr<T>& ap) = delete;

	private:
		T* _ptr;
	};


	template<class T>
	class shared_ptr
	{
	public:

    		shared_ptr(T* ptr = nullptr)
                    :_ptr(ptr)
	            ,_pcount(new int(1))
		{}    

                template<class D>
		shared_ptr(T* ptr,D del)
		    :_ptr(ptr)
		    ,_pcount(new int(1))
                    ,_del(del)
                {}

		~shared_ptr()
		{
                    if(--(*_pcount) == 0)
                    {
                        std::cout << "delete:" << std::endl;
                       // delete _ptr;
		        _del(_ptr);
                        delete _pcount;
                    }
                }

		T& operator*()
		{
	            return *_ptr;
		}

		T* operator->()
		{
		    return _ptr;
		}

		shared_ptr(const shared_ptr<T>& sp)
                    :_ptr(sp._ptr)
                    ,_pcount(sp.pcount)
                {
                    (*_pcount)++;
                }
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
                {
                    if(sp._ptr == _ptr)
                        return *this;
                    
                    if(--(*_pcount) == 0)
                    {
                        delete _ptr;
                        delete _pcount;
                    }

                    ++(*sp._pcount);
                    _ptr = sp._ptr;
                    _pcount = sp._pcount;

                    return *this;

                }

                int use_count() const
                {
                    return *_pcount;
                }

                T* get() const
                {
                    return _ptr;
                }
	private:
		T* _ptr;
                int* _pcount;
                function<void(T*)> _del = [](T* ptr){delete ptr;};
	};

        //weak_ptr不支持RAII，但是它像指针一样
	template<class T>
	class weak_ptr
        {
        public:
                weak_ptr(const shared_ptr<T>& sp)
                    :_ptr(sp.get())
                {}

                shared_ptr<T>& operator=(const shared_ptr<T>& sp)
                {
                    _ptr = sp.get();
                    return *this;
                }

                weak_ptr()
                    :_ptr(nullptr)
                {}

        	T& operator*()
		{
	            return *_ptr;
		}

		T* operator->()
		{
		    return _ptr;
		}
        
        
        private:
            T* _ptr;
        };
}
